package dfsketches.mineral_generator;

import java.util.Arrays;
import java.util.Random;

public class VolumeMineralGenerator implements MineralGenerator {

	public VolumeMineralGenerator(int[][][] volume) {
		vol = volume;
	}
	
	@Override
	public void addVein(int len, float scale, float wide, BoundingBox box,
			int color) {
		int[] c = new int[len];
		c[0] = 1;
		for (int i = 1; i < len; i++)
			for (int j = len - 1; j > 0; j--)
				c[j] = c[j] + c[j - 1];
		
		int bound = (int)wide * 2;
		wide *= wide;
		
		int w = box.w();
		int h = box.h();
		int d = box.d();

		Random rand = new Random();
		float[] x = new float[len];
		float[] y = new float[len];
		float[] z = new float[len];
		x[0] = rand.nextFloat() * w;
		y[0] = rand.nextFloat() * h;
		z[0] = rand.nextFloat() * d;
		for (int k = 1; k < len; k++) {
			x[k] = x[k - 1] + rand.nextFloat() * scale - scale / 2;
			y[k] = y[k - 1] + rand.nextFloat() * scale - scale / 2;
			z[k] = z[k - 1] + rand.nextFloat() * scale - scale / 2;
		}
		
		float[][][] psi = new float[w][h][d];
		for (int i = 0; i < w; i++)
			for (int j = 0; j < h; j++)
				Arrays.fill(psi[i][j], Float.MAX_VALUE);
		
		float delta = 1 / scale / len;
		for (float t = 0; t < 1 - delta/2; t += delta) {
			float xt = 0;
			float yt = 0;
			float zt = 0;
			float pow1 = 1;
			float pow2 = (float)Math.pow(1 - t, len - 1);
			float div = 1 / (1-t);
			float m;
			for (int k = 0; k < len; k++) {
				m = c[k] * pow1 * pow2;
				xt += m * x[k];
				yt += m * y[k];
				zt += m * z[k];
				pow1 *= t;
				pow2 *= div;
			}
			
			int rbound = Math.max((int)xt - bound, 0);
			int lbound = Math.min((int)xt + bound, w);
			int ubound = Math.max((int)yt - bound, 0);
			int bbound = Math.min((int)yt + bound, h);
			int nbound = Math.max((int)zt - bound, 0);
			int fbound = Math.min((int)zt + bound, d);
			for (int i = rbound; i < lbound; i++) {
				for (int j = ubound; j < bbound; j++) {
					for (int k = nbound; k < fbound; k++) {
						float r = (xt - i) * (xt - i) + (yt - j) * (yt - j) + (zt - k) * (zt - k);
						psi[i][j][k] = Math.min(psi[i][j][k], r);
					}
				}
			}
		}
		
		int sx = box.x();
		int sy = box.y();
		int sz = box.z();
		for (int i = 0; i < w; i++)
			for (int j = 0; j < h; j++)
				for (int k = 0; k < d; k++) {
					float arg = psi[i][j][k] / wide * 2f;
					float p = (float)Math.exp(-arg * arg);
					if (rand.nextFloat() <= p) vol[i+sx][j+sy][k+sz] = color;
				}
	}

	/**
	 * Not implemented yet.
	 */
	@Override
	public void addSpot(int complexity, float wide, BoundingBox box, int color) {
		// TODO Auto-generated method stub

	}

	@Override
	public void addCluster(int complexity, float wide, float dist, BoundingBox box, int color) {
		int w = box.w();
		int h = box.h();
		int d = box.d();
		
		Random rand = new Random();
		float[] x = new float[complexity];
		float[] y = new float[complexity];
		float[] z = new float[complexity];
		x[0] = rand.nextFloat() * w;
		y[0] = rand.nextFloat() * h;
		z[0] = rand.nextFloat() * d;
		for (int k = 1; k < complexity; k++) {
			x[k] = x[k - 1] + rand.nextFloat() * dist - dist / 2;
			y[k] = y[k - 1] + rand.nextFloat() * dist - dist / 2;
			z[k] = z[k - 1] + rand.nextFloat() * dist - dist / 2;
		}

		float[][][] psi = new float[w][h][d];
		for (int i = 0; i < w; i++)
			for (int j = 0; j < h; j++)
				Arrays.fill(psi[i][j], 1);
		
		for (int t = 0; t < complexity; t++) {
			float dx = x[t];
			for (int i = 0; i < w; i++) {
				float dy = y[t];
				for (int j = 0; j < h; j++) {
					float dz = z[t];
					for (int k = 0; k < d; k++) {
						psi[i][j][k] *= (dx * dx + dy * dy + dz * dz) / wide;
						dz -= 1f;
					}
					dy -= 1f;
				}
				dx -= 1f;
			}
		}
		
		int sx = box.x();
		int sy = box.y();
		int sz = box.z();
		for (int i = 0; i < box.w(); i++)
			for (int j = 0; j < box.h(); j++)
				for(int k = 0; k < box.d(); k++) {
					float p = 1f - (float)Math.exp(-1 / Math.pow(psi[i][j][k], 15));
					if (rand.nextFloat() <= p) vol[i+sx][j+sy][k+sz] = color;
				}
	}
	
	@Override
	public void addFullCluster(int complexity, float wide, float dist,
			BoundingBox box, int color) {
		int w = box.w();
		int h = box.h();
		int d = box.d();
		int sx = box.x();
		int sy = box.y();
		int sz = box.z();
		
		Random rand = new Random();
		float[] x = new float[complexity];
		float[] y = new float[complexity];
		float[] z = new float[complexity];
		float border = dist + wide;
		x[0] = rand.nextFloat() * (w - 2 * border) + border;
		y[0] = rand.nextFloat() * (h - 2 * border) + border;
		z[0] = rand.nextFloat() * (d - 2 * border) + border;
		for (int k = 1; k < complexity; k++) {
			x[k] = x[0] + rand.nextFloat() * dist - dist / 2;
			y[k] = y[0] + rand.nextFloat() * dist - dist / 2;
			z[k] = z[0] + rand.nextFloat() * dist - dist / 2;
		}

		wide *= wide;
		
		float[][][] psi = new float[w][h][d];
		for (int i = 0; i < w; i++)
			for (int j = 0; j < h; j++)
				Arrays.fill(psi[i][j], 1);
		
		for (int t = 0; t < complexity; t++) {
			float dx = x[t];
			for (int i = 0; i < w; i++) {
				float dy = y[t];
				for (int j = 0; j < h; j++) {
					float dz = z[t];
					for (int k = 0; k < d; k++) {
						psi[i][j][k] *= (dx * dx + dy * dy + dz * dz) / wide;
						dz -= 1f;
					}
					dy -= 1f;
				}
				dx -= 1f;
			}
		}
		
		for (int i = 0; i < w; i++)
			for (int j = 0; j < h; j++)
				for(int k = 0; k < d; k++) {
					float p = 1f - (float)Math.exp(-1 / Math.pow(psi[i][j][k], 15));
					if (rand.nextFloat() <= p) vol[i+sx][j+sy][k+sz] = color;
				}
	}

	@Override
	public void addPlacer(float radius, float probability, BoundingBox box,
			int color) {
		// TODO Auto-generated method stub

	}

	private int[][][] vol;
}